﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Security.Cryptography;
using System.Text;

/// <summary>
/// 加密工具类
/// </summary>
public static class CommonEncrypt
{
    //-----不可逆加密-----//

    #region MD5加密（更快）

    /// <summary>
    /// MD5加密
    /// </summary>
    public static string Md532(this string value)
    {
        if (value == null)
        {
            throw new ArgumentNullException("未将对象引用设置到对象的实例。");
        }

        var encoding = Encoding.UTF8;
        MD5 md5 = MD5.Create();
        return HashAlgorithmBase(md5, value, encoding);
    }

    /// <summary>
    /// 加权MD5加密
    /// </summary>
    public static string Md532(this string value, string salt)
    {
        return salt == null ? value.Md532() : (value + "『" + salt + "』").Md532();
    }

    #endregion

    #region SHA 加密（更强）

    /// <summary>
    /// SHA1 加密
    /// </summary>
    public static string Sha1(this string value)
    {
        if (value == null)
        {
            throw new ArgumentNullException("未将对象引用设置到对象的实例。");
        }

        var encoding = Encoding.UTF8;
        SHA1 sha1 = new SHA1CryptoServiceProvider();
        return HashAlgorithmBase(sha1, value, encoding);
    }

    /// <summary>
    /// SHA256 加密
    /// </summary>
    public static string Sha256(this string value)
    {
        if (value == null)
        {
            throw new ArgumentNullException("未将对象引用设置到对象的实例。");
        }

        var encoding = Encoding.UTF8;
        SHA256 sha256 = new SHA256Managed();
        return HashAlgorithmBase(sha256, value, encoding);
    }

    /// <summary>
    /// SHA512 加密
    /// </summary>
    public static string Sha512(this string value)
    {
        if (value == null)
        {
            throw new ArgumentNullException("未将对象引用设置到对象的实例。");
        }
        var encoding = Encoding.UTF8;
        SHA512 sha512 = new SHA512Managed();
        return HashAlgorithmBase(sha512, value, encoding);
    }

    #endregion

    //----- 可逆加密 -----//

    #region AES 加密解密（最强）

    private const string DEFAULT_KEY = "asdf";
    private const string DEFAULT_IV = "7456";

    /// <summary>  
    /// AES加密  
    /// </summary>  
    /// <param name="value">待加密字段</param>  
    /// <param name="keyVal">密钥值</param>  
    /// <param name="ivVal">加密辅助向量</param> 
    /// <returns></returns>  
    public static string AesStr(this string value, string keyVal, string ivVal)
    {
        if (value == null)
        {
            throw new ArgumentNullException("未将对象引用设置到对象的实例。");
        }

        var encoding = Encoding.UTF8;
        byte[] btKey = keyVal.FormatByte(encoding);
        byte[] btIv = ivVal.FormatByte(encoding);
        byte[] byteArray = encoding.GetBytes(value);
        string encrypt;
        Rijndael aes = Rijndael.Create();
        using (MemoryStream mStream = new MemoryStream())
        {
            using (CryptoStream cStream = new CryptoStream(mStream, aes.CreateEncryptor(btKey, btIv), CryptoStreamMode.Write))
            {
                cStream.Write(byteArray, 0, byteArray.Length);
                cStream.FlushFinalBlock();
                encrypt = Convert.ToBase64String(mStream.ToArray());
            }
        }
        aes.Clear();
        return encrypt;
    }

    /// <summary>  
    /// AES解密  
    /// </summary>  
    /// <param name="value">待加密字段</param>  
    /// <param name="keyVal">密钥值</param>  
    /// <param name="ivVal">加密辅助向量</param>  
    /// <returns></returns>  
    public static string UnAesStr(this string value, string keyVal, string ivVal)
    {
        var encoding = Encoding.UTF8;
        byte[] btKey = keyVal.FormatByte(encoding);
        byte[] btIv = ivVal.FormatByte(encoding);
        byte[] byteArray = Convert.FromBase64String(value);
        string decrypt;
        Rijndael aes = Rijndael.Create();
        using (MemoryStream mStream = new MemoryStream())
        {
            using (CryptoStream cStream = new CryptoStream(mStream, aes.CreateDecryptor(btKey, btIv), CryptoStreamMode.Write))
            {
                cStream.Write(byteArray, 0, byteArray.Length);
                cStream.FlushFinalBlock();
                decrypt = encoding.GetString(mStream.ToArray());
            }
        }
        aes.Clear();
        return decrypt;
    }

    /// <summary>  
    /// AES Byte类型 加密  
    /// </summary>  
    /// <param name="data">待加密明文</param>  
    /// <param name="keyVal">密钥值</param>  
    /// <param name="ivVal">加密辅助向量</param>  
    /// <returns></returns>  
    public static byte[] AesByte(this byte[] data, string keyVal = DEFAULT_KEY, string ivVal = DEFAULT_IV)
    {
        byte[] bKey = new byte[32];
        Array.Copy(Encoding.UTF8.GetBytes(keyVal.PadRight(bKey.Length)), bKey, bKey.Length);
        byte[] bVector = new byte[16];
        Array.Copy(Encoding.UTF8.GetBytes(ivVal.PadRight(bVector.Length)), bVector, bVector.Length);
        byte[] cryptograph;
        Rijndael aes = Rijndael.Create();
        try
        {
            using (MemoryStream mStream = new MemoryStream())
            {
                using (CryptoStream cStream = new CryptoStream(mStream, aes.CreateEncryptor(bKey, bVector), CryptoStreamMode.Write))
                {
                    cStream.Write(data, 0, data.Length);
                    cStream.FlushFinalBlock();
                    cryptograph = mStream.ToArray();
                }
            }
        }
        catch
        {
            cryptograph = null;
        }
        return cryptograph;
    }

    /// <summary>  
    /// AES Byte类型 解密  
    /// </summary>  
    /// <param name="data">待解密明文</param>  
    /// <param name="keyVal">密钥值</param>  
    /// <param name="ivVal">加密辅助向量</param> 
    /// <returns></returns>  
    public static byte[] UnAesByte(this byte[] data, string keyVal = DEFAULT_KEY, string ivVal = DEFAULT_IV)
    {
        byte[] bKey = new byte[32];
        Array.Copy(Encoding.UTF8.GetBytes(keyVal.PadRight(bKey.Length)), bKey, bKey.Length);
        byte[] bVector = new byte[16];
        Array.Copy(Encoding.UTF8.GetBytes(ivVal.PadRight(bVector.Length)), bVector, bVector.Length);
        byte[] original;
        Rijndael aes = Rijndael.Create();
        try
        {
            using (MemoryStream mStream = new MemoryStream(data))
            {
                using (CryptoStream cStream = new CryptoStream(mStream, aes.CreateDecryptor(bKey, bVector), CryptoStreamMode.Read))
                {
                    using (MemoryStream originalMemory = new MemoryStream())
                    {
                        byte[] buffer = new byte[1024];
                        int readBytes;
                        while ((readBytes = cStream.Read(buffer, 0, buffer.Length)) > 0)
                        {
                            originalMemory.Write(buffer, 0, readBytes);
                        }

                        original = originalMemory.ToArray();
                    }
                }
            }
        }
        catch
        {
            original = null;
        }
        return original;
    }

    #endregion

    #region DES 加密解密

    /// <summary>
    /// DES 加密
    /// </summary>
    public static string Des(this string value, string keyVal, string ivVal)
    {
        try
        {
            byte[] data = Encoding.UTF8.GetBytes(value);
            var des = new DESCryptoServiceProvider { Key = Encoding.ASCII.GetBytes(keyVal.Length > 8 ? keyVal.Substring(0, 8) : keyVal), IV = Encoding.ASCII.GetBytes(ivVal.Length > 8 ? ivVal.Substring(0, 8) : ivVal) };
            var desencrypt = des.CreateEncryptor();
            byte[] result = desencrypt.TransformFinalBlock(data, 0, data.Length);
            return BitConverter.ToString(result);
        }
        catch { return "转换出错！"; }
    }

    /// <summary>
    /// DES 解密
    /// </summary>
    public static string UnDes(this string value, string keyVal, string ivVal)
    {
        try
        {
            string[] sInput = value.Split("-".ToCharArray());
            byte[] data = new byte[sInput.Length];
            for (int i = 0; i < sInput.Length; i++)
            {
                data[i] = byte.Parse(sInput[i], NumberStyles.HexNumber);
            }
            var des = new DESCryptoServiceProvider { Key = Encoding.ASCII.GetBytes(keyVal.Length > 8 ? keyVal.Substring(0, 8) : keyVal), IV = Encoding.ASCII.GetBytes(ivVal.Length > 8 ? ivVal.Substring(0, 8) : ivVal) };
            var desencrypt = des.CreateDecryptor();
            byte[] result = desencrypt.TransformFinalBlock(data, 0, data.Length);
            return Encoding.UTF8.GetString(result);
        }
        catch { return "解密出错！"; }
    }

    #endregion

    #region BASE64 加密解密

    /// <summary>
    /// BASE64 加密
    /// </summary>
    /// <param name="value">待加密字段</param>
    /// <returns></returns>
    public static string Base64(this string value)
    {
        var btArray = Encoding.UTF8.GetBytes(value);
        return Convert.ToBase64String(btArray, 0, btArray.Length);
    }

    /// <summary>
    /// BASE64 解密
    /// </summary>
    /// <param name="value">待解密字段</param>
    /// <returns></returns>
    public static string UnBase64(this string value)
    {
        var btArray = Convert.FromBase64String(value);
        return Encoding.UTF8.GetString(btArray);
    }

    #endregion

    //----- 内部方法 -----//

    #region 内部方法

    /// <summary>
    /// 转成数组
    /// </summary>
    private static byte[] Str2Bytes(this string source)
    {
        source = source.Replace(" ", "");
        byte[] buffer = new byte[source.Length / 2];
        for (int i = 0; i < source.Length; i += 2) buffer[i / 2] = Convert.ToByte(source.Substring(i, 2), 16);
        return buffer;
    }

    /// <summary>
    /// 转换成字符串
    /// </summary>
    private static string Bytes2Str(this IEnumerable<byte> source, string formatStr = "{0:X2}")
    {
        StringBuilder pwd = new StringBuilder();
        foreach (byte btStr in source) { pwd.AppendFormat(formatStr, btStr); }
        return pwd.ToString();
    }

    /// <summary>
    /// 格式化字符串为字节数组
    /// </summary>
    /// <param name="strVal">字符串</param>
    /// <param name="encoding">编码</param>
    /// <returns></returns>
    private static byte[] FormatByte(this string strVal, Encoding encoding)
    {
        return encoding.GetBytes(strVal.Base64().Substring(0, 16).ToUpper());
    }

    /// <summary>
    /// HashAlgorithm 加密统一方法
    /// </summary>
    private static string HashAlgorithmBase(HashAlgorithm hashAlgorithmObj, string source, Encoding encoding)
    {
        byte[] btStr = encoding.GetBytes(source);
        byte[] hashStr = hashAlgorithmObj.ComputeHash(btStr);
        return hashStr.Bytes2Str();
    }

    #endregion
}